/***************************************************************************
 *
 *   Copyright (C) 2012 by Ben Barsdell and Andrew Jameson
 *   Licensed under the Academic Free License version 2.1
 *
 ***************************************************************************/

#include <stdio.h>
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
#include <string.h>
#include <sstream>
#include <string>
using std::string;
#include <cstdlib>

#include "pipeline/default_params.h"
#include "pipeline/maths.h"
#include "pipeline/parse_command_line.h"

int hd_parse_command_line(int argc, char* argv[], hd_params* params)
{
    // TODO: Make this robust to malformed input
    size_t i = 0;
    while (++i < (size_t)argc) {
        if (argv[i] == string("-h")) {
            hd_print_usage();
            return -1;
        }
        if (argv[i] == string("-v")) {
            params->verbosity = max(params->verbosity, 1);
        } else if (argv[i] == string("-V")) {
            params->verbosity = max(params->verbosity, 2);
        } else if (argv[i] == string("-g")) {
            params->verbosity = max(params->verbosity, 3);
        } else if (argv[i] == string("-G")) {
            params->verbosity = max(params->verbosity, 4);
        }
#ifdef HAVE_PSRDADA
        else if (argv[i] == string("-k")) {
            sscanf(argv[++i], "%x", &(params->dada_id));
        }
#endif
        else if (argv[i] == string("-f")) {
            params->sigproc_file = strdup(argv[++i]);
        } else if (argv[i] == string("-yield_cpu")) {
            params->yield_cpu = true;
        } else if (argv[i] == string("-nsamps_gulp")) {
            params->nsamps_gulp = atoi(argv[++i]);
        } else if (argv[i] == string("-baseline_length")) {
            params->baseline_length = atof(argv[++i]);
        } else if (argv[i] == string("-dm")) {
            params->dm_min = atof(argv[++i]);
            params->dm_max = atof(argv[++i]);
        } else if (argv[i] == string("-dm_tol")) {
            params->dm_tol = atof(argv[++i]);
        } else if (argv[i] == string("-dm_pulse_width")) {
            params->dm_pulse_width = atof(argv[++i]);
        } else if (argv[i] == string("-dm_nbits")) {
            params->dm_nbits = atoi(argv[++i]);
        } else if (argv[i] == string("-gpu_id")) {
            params->gpu_id = atoi(argv[++i]);
        } else if (argv[i] == string("-no_scrunching")) {
            params->use_scrunching = false;
        } else if (argv[i] == string("-scrunch_tol")) {
            params->scrunch_tol = atof(argv[++i]);
        } else if (argv[i] == string("-rfi_tol")) {
            params->rfi_tol = atof(argv[++i]);
        } else if (argv[i] == string("-rfi_min_beams")) {
            params->rfi_min_beams = atoi(argv[++i]);
        } else if (argv[i] == string("-rfi_no_narrow")) {
            params->rfi_narrow = false;
        } else if (argv[i] == string("-rfi_no_broad")) {
            params->rfi_broad = false;
        } else if (argv[i] == string("-boxcar_max")) {
            params->boxcar_max = atoi(argv[++i]);
        } else if (argv[i] == string("-detect_thresh")) {
            params->detect_thresh = atof(argv[++i]);
        } else if (argv[i] == string("-beam")) {
            params->beam = atoi(argv[++i]) - 1;
            params->override_beam = true;
        } else if (argv[i] == string("-beam_count")) {
            params->beam_count = atoi(argv[++i]);
        } else if (argv[i] == string("-cand_sep_time")) {
            params->cand_sep_time = atoi(argv[++i]);
        } else if (argv[i] == string("-cand_sep_filter")) {
            params->cand_sep_filter = atoi(argv[++i]);
        } else if (argv[i] == string("-cand_sep_dm_trial")) {
            params->cand_sep_dm = atoi(argv[++i]);
        } else if (argv[i] == string("-cand_rfi_dm_cut")) {
            params->cand_rfi_dm_cut = atof(argv[++i]);
        } else if (argv[i] == string("-max_giant_rate")) {
            params->max_giant_rate = atof(argv[++i]);
        } else if (argv[i] == string("-output_dir")) {
            params->output_dir = strdup(argv[++i]);
        } else if (argv[i] == string("-min_tscrunch_width")) {
            params->min_tscrunch_width = atoi(argv[++i]);
        } else if (argv[i] == string("-coincidencer")) {
            std::istringstream iss(argv[++i], std::istringstream::in);
            string host, port;
            getline(iss, host, ':');
            getline(iss, port);
            params->coincidencer_host = strdup(host.c_str());
            params->coincidencer_port = atoi(port.c_str());
        } else if (argv[i] == string("-fswap")) {
            params->fswap = true;
        } else if (argv[i] == string("-boxcar_renorm")) {
            params->boxcar_renorm = true;
        } else if (argv[i] == string("-zap_chans")) {
            unsigned int izap = params->num_channel_zaps;
            params->num_channel_zaps++;
            params->channel_zaps =
                (hd_range_t*)realloc(params->channel_zaps,
                                     sizeof(hd_range_t) * params->num_channel_zaps);
            params->channel_zaps[izap].start = atoi(argv[++i]);
            params->channel_zaps[izap].end = atoi(argv[++i]);
        } else {
            cerr << "WARNING: Unknown parameter '" << argv[i] << "'" << endl;
        }
    }

    if (params->sigproc_file == NULL) {
#ifdef HAVE_PSRDADA
        if (params->dada_id != 0)
            return 0;
#endif
        cerr << "ERROR: no input mechanism specified" << endl;
        hd_print_usage();
        return -1;
    } else
        return 0;
}

void hd_print_usage()
{
    hd_params p;
    hd_set_default_params(&p);

    cout << "Usage: heimdall [options]" << endl;
    cout << "    -k  key                  use PSRDADA hexidecimal key" << endl;
    cout << "    -f  filename             process specified SIGPROC filterbank file"
         << endl;
    cout << "    -vVgG                    increase verbosity level" << endl;
    cout << "    -yield_cpu               yield CPU during GPU operations" << endl;
    cout << "    -gpu_id ID               run on specified GPU" << endl;
    cout << "    -nsamps_gulp num         number of samples to be read at a time ["
         << p.nsamps_gulp << "]" << endl;
    cout << "    -baseline_length num     number of seconds over which to smooth the "
            "baseline ["
         << p.baseline_length << "]" << endl;
    cout << "    -beam ##                 over-ride beam number" << endl;
    cout << "    -output_dir path         create all output files in specified path"
         << endl;
    cout << "    -dm min max              min and max DM" << endl;
    cout << "    -dm_tol num              SNR loss tolerance between each DM trial ["
         << p.dm_tol << "]" << endl;
    cout << "    -coincidencer host:port  connect to the coincidencer on the specified "
            "host and port"
         << endl;
    cout << "    -zap_chans start end     zap all channels between start and end "
            "channels inclusive"
         << endl;
    cout << "    -max_giant_rate nevents  limit the maximum number of individual "
            "detections per minute to nevents"
         << endl;
    cout << "    -dm_pulse_width num      expected intrinsic width of the pulse signal "
            "in microseconds"
         << endl;
    cout << "    -dm_nbits num            number of bits per sample in dedispersed time "
            "series ["
         << p.dm_nbits << "]" << endl;
    cout << "    -no_scrunching           don't use an adaptive time scrunching during "
            "dedispersion"
         << endl;
    cout << "    -scrunching_tol num      smear tolerance factor for time scrunching ["
         << p.scrunch_tol << "]" << endl;
    cout << "    -rfi_tol num             RFI exicision threshold limits [" << p.rfi_tol
         << "]" << endl;
    cout << "    -rfi_no_narrow           disable narrow band RFI excision" << endl;
    cout << "    -rfi_no_broad            disable 0-DM RFI excision" << endl;
    cout << "    -boxcar_max num          maximum boxcar width in samples ["
         << p.boxcar_max << "]" << endl;
    cout << "    -fswap                   swap channel ordering for negative DM - "
            "SIGPROC 2,4 or 8 bit only"
         << endl;
    cout << "    -boxcar_renorm           renormalise the boxcar filtered timeseries "
            "instead of rescale"
         << endl;
    cout << "    -min_tscrunch_width num  vary between high quality (large value) and "
            "high performance (low value)"
         << endl;
}
